home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / lib / gedit-2 / plugins / snippets / Document.py < prev    next >
Encoding:
Python Source  |  2009-04-14  |  35.8 KB  |  854 lines

  1. #    Gedit snippets plugin
  2. #    Copyright (C) 2005-2006  Jesse van den Kieboom <jesse@icecrew.nl>
  3. #
  4. #    This program is free software; you can redistribute it and/or modify
  5. #    it under the terms of the GNU General Public License as published by
  6. #    the Free Software Foundation; either version 2 of the License, or
  7. #    (at your option) any later version.
  8. #
  9. #    This program is distributed in the hope that it will be useful,
  10. #    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. #    GNU General Public License for more details.
  13. #
  14. #    You should have received a copy of the GNU General Public License
  15. #    along with this program; if not, write to the Free Software
  16. #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  17.  
  18. import os
  19. import re
  20.  
  21. import gtk
  22. from gtk import gdk
  23. import gio
  24. import gedit
  25.  
  26. from Library import Library
  27. from Snippet import Snippet
  28. from Placeholder import *
  29. from SnippetComplete import SnippetComplete
  30.  
  31. class Document:
  32.         TAB_KEY_VAL = (gtk.keysyms.Tab, \
  33.                         gtk.keysyms.ISO_Left_Tab)
  34.         SPACE_KEY_VAL = (gtk.keysyms.space,)
  35.         
  36.         def __init__(self, instance, view):
  37.                 self.view = None
  38.                 self.instance = instance
  39.                 
  40.                 self.placeholders = []
  41.                 self.active_snippets = []
  42.                 self.active_placeholder = None
  43.                 self.signal_ids = {}
  44.                 
  45.                 self.ordered_placeholders = []
  46.                 self.update_placeholders = []
  47.                 self.jump_placeholders = []
  48.                 self.language_id = 0
  49.                 self.timeout_update_id = 0
  50.                 
  51.                 # Always have a reference to the global snippets
  52.                 Library().ref(None)
  53.                 self.set_view(view)
  54.         
  55.         # Stop controlling the view. Remove all active snippets, remove references
  56.         # to the view and the plugin instance, disconnect all signal handlers
  57.         def stop(self):
  58.                 if self.timeout_update_id != 0:
  59.                         gobject.source_remove(self.timeout_update_id)
  60.                         self.timeout_update_id = 0
  61.                         del self.update_placeholders[:]
  62.                         del self.jump_placeholders[:]
  63.  
  64.                 # Always release the reference to the global snippets
  65.                 Library().unref(None)
  66.                 self.set_view(None)
  67.                 self.instance = None
  68.                 self.active_placeholder = None
  69.  
  70.         def disconnect_signal(self, obj, signal):
  71.                 if (obj, signal) in self.signal_ids:
  72.                         obj.disconnect(self.signal_ids[(obj, signal)])
  73.                         del self.signal_ids[(obj, signal)]
  74.                 
  75.         def connect_signal(self, obj, signal, cb):
  76.                 self.disconnect_signal(obj, signal)     
  77.                 self.signal_ids[(obj, signal)] = obj.connect(signal, cb)
  78.  
  79.         def connect_signal_after(self, obj, signal, cb):
  80.                 self.disconnect_signal(obj, signal)
  81.                 self.signal_ids[(obj, signal)] = obj.connect_after(signal, cb)
  82.                 
  83.         # Set the view to be controlled. Installs signal handlers and sets current
  84.         # language. If there is already a view set this function will first remove
  85.         # all currently active snippets and disconnect all current signals. So
  86.         # self.set_view(None) will effectively remove all the control from the
  87.         # current view
  88.         def _set_view(self, view):
  89.                 if self.view:
  90.                         buf = self.view.get_buffer()
  91.                         
  92.                         # Remove signals
  93.                         signals = {self.view: ('key-press-event', 'destroy', 
  94.                                                'notify::editable', 'drag-data-received'),
  95.                                    buf:       ('notify::language', 'changed', 'cursor-moved', 'insert-text')}
  96.                         
  97.                         for obj, sig in signals.items():
  98.                                 for s in sig:
  99.                                         self.disconnect_signal(obj, s)
  100.                         
  101.                         # Remove all active snippets
  102.                         for snippet in list(self.active_snippets):
  103.                                 self.deactivate_snippet(snippet, True)
  104.  
  105.                 self.view = view
  106.                 
  107.                 if view != None:
  108.                         buf = view.get_buffer()
  109.                        
  110.                         self.connect_signal(view, 'destroy', self.on_view_destroy)
  111.  
  112.                         if view.get_editable():
  113.                                 self.connect_signal(view, 'key-press-event', self.on_view_key_press)
  114.  
  115.                         self.connect_signal(buf, 'notify::language', self.on_notify_language)
  116.                         self.connect_signal(view, 'notify::editable', self.on_notify_editable)
  117.                         self.connect_signal(view, 'drag-data-received', self.on_drag_data_received)
  118.  
  119.                         self.update_language()
  120.                 elif self.language_id != 0:
  121.                         langid = self.language_id
  122.                         
  123.                         self.language_id = None;
  124.                         
  125.                         if self.instance:
  126.                                 self.instance.language_changed(self)
  127.  
  128.                         Library().unref(langid)
  129.  
  130.         def set_view(self, view):
  131.                 if view == self.view:
  132.                         return
  133.                 
  134.                 self._set_view(view)
  135.  
  136.         # Call this whenever the language in the view changes. This makes sure that
  137.         # the correct language is used when finding snippets
  138.         def update_language(self):
  139.                 lang = self.view.get_buffer().get_language()
  140.  
  141.                 if lang == None and self.language_id == None:
  142.                         return
  143.                 elif lang and lang.get_id() == self.language_id:
  144.                         return
  145.  
  146.                 langid = self.language_id
  147.  
  148.                 if lang:
  149.                         self.language_id = lang.get_id()
  150.                 else:
  151.                         self.language_id = None
  152.  
  153.                 if self.instance:
  154.                         self.instance.language_changed(self)
  155.  
  156.                 if langid != 0:
  157.                         Library().unref(langid)
  158.  
  159.                 Library().ref(self.language_id)
  160.  
  161.         def accelerator_activate(self, keyval, mod):
  162.                 if not self.view or not self.view.get_editable():
  163.                         return
  164.  
  165.                 accelerator = gtk.accelerator_name(keyval, mod)
  166.                 snippets = Library().from_accelerator(accelerator, \
  167.                                 self.language_id)
  168.  
  169.                 snippets_debug('Accel!')
  170.  
  171.                 if len(snippets) == 0:
  172.                         return False
  173.                 elif len(snippets) == 1:
  174.                         self.apply_snippet(snippets[0])
  175.                 else:
  176.                         # Do the fancy completion dialog
  177.                         return self.show_completion(snippets)
  178.  
  179.                 return True
  180.  
  181.         def first_snippet_inserted(self):
  182.                 buf = self.view.get_buffer()
  183.                 
  184.                 self.connect_signal(buf, 'changed', self.on_buffer_changed)
  185.                 self.connect_signal(buf, 'cursor-moved', self.on_buffer_cursor_moved)
  186.                 self.connect_signal_after(buf, 'insert-text', self.on_buffer_insert_text)
  187.         
  188.         def last_snippet_removed(self):
  189.                 buf = self.view.get_buffer()
  190.                 self.disconnect_signal(buf, 'changed')
  191.                 self.disconnect_signal(buf, 'cursor-moved')
  192.                 self.disconnect_signal(buf, 'insert-text')
  193.  
  194.         def current_placeholder(self):
  195.                 buf = self.view.get_buffer()
  196.                 
  197.                 piter = buf.get_iter_at_mark(buf.get_insert())        
  198.                 found = []
  199.  
  200.                 for placeholder in self.placeholders:
  201.                         begin = placeholder.begin_iter()
  202.                         end = placeholder.end_iter()
  203.  
  204.                         if piter.compare(begin) >= 0 and piter.compare(end) <= 0:
  205.                                 found.append(placeholder)
  206.  
  207.                 if self.active_placeholder in found:
  208.                         return self.active_placeholder
  209.                 elif len(found) > 0:
  210.                         return found[0]
  211.                 else:
  212.                         return None
  213.  
  214.         def advance_placeholder(self, direction):
  215.                 # Returns (CurrentPlaceholder, NextPlaceholder), depending on direction
  216.                 buf = self.view.get_buffer()
  217.                 
  218.                 piter = buf.get_iter_at_mark(buf.get_insert())
  219.                 found = current = next = None
  220.                 length = len(self.placeholders)
  221.                 
  222.                 placeholders = list(self.placeholders)
  223.                 
  224.                 if self.active_placeholder:
  225.                         begin = self.active_placeholder.begin_iter()
  226.                         end = self.active_placeholder.end_iter()
  227.                         
  228.                         if piter.compare(begin) >= 0 and piter.compare(end) <= 0:
  229.                                 current = self.active_placeholder
  230.                                 currentIndex = placeholders.index(self.active_placeholder)
  231.  
  232.                 if direction == 1:
  233.                         # w = piter, x = begin, y = end, z = found
  234.                         nearest = lambda w, x, y, z: (w.compare(x) <= 0 and (not z or \
  235.                                         x.compare(z.begin_iter()) < 0))
  236.                         indexer = lambda x: x < length - 1
  237.                 else:
  238.                         # w = piter, x = begin, y = end, z = prev
  239.                         nearest = lambda w, x, y, z: (w.compare(x) >= 0 and (not z or \
  240.                                         x.compare(z.begin_iter()) >= 0))
  241.                         indexer = lambda x: x > 0
  242.  
  243.                 for index in range(0, length):
  244.                         placeholder = placeholders[index]
  245.                         begin = placeholder.begin_iter()
  246.                         end = placeholder.end_iter()
  247.                         
  248.                         # Find the nearest placeholder
  249.                         if nearest(piter, begin, end, found):
  250.                                 foundIndex = index
  251.                                 found = placeholder
  252.                         
  253.                         # Find the current placeholder
  254.                         if piter.compare(begin) >= 0 and \
  255.                                         piter.compare(end) <= 0 and \
  256.                                         current == None:
  257.                                 currentIndex = index
  258.                                 current = placeholder
  259.                 
  260.                 if current and current != found and \
  261.                    (current.begin_iter().compare(found.begin_iter()) == 0 or \
  262.                     current.end_iter().compare(found.begin_iter()) == 0) and \
  263.                    self.active_placeholder and \
  264.                    current.begin_iter().compare(self.active_placeholder.begin_iter()) == 0:
  265.                         # if current and found are at the same place, then
  266.                         # resolve the 'hugging' problem
  267.                         current = self.active_placeholder
  268.                         currentIndex = placeholders.index(current)
  269.  
  270.                         found = current
  271.                 
  272.                 if current and current == found:
  273.                         if indexer(currentIndex):
  274.                                 next = placeholders[currentIndex + direction]
  275.                 elif found:
  276.                         next = found
  277.                 elif length > 0:
  278.                         next = self.placeholders[0]
  279.                 
  280.                 return current, next
  281.         
  282.         def next_placeholder(self):
  283.                 return self.advance_placeholder(1)
  284.         
  285.         def previous_placeholder(self):
  286.                 return self.advance_placeholder(-1)
  287.  
  288.         def cursor_on_screen(self):
  289.                 buf = self.view.get_buffer()
  290.                 self.view.scroll_mark_onscreen(buf.get_insert())
  291.         
  292.         def set_active_placeholder(self, placeholder):
  293.                 self.active_placeholder = placeholder
  294.  
  295.         def goto_placeholder(self, current, next):
  296.                 last = None
  297.  
  298.                 if current:
  299.                         # Signal this placeholder to end action
  300.                         current.leave()
  301.                         
  302.                         if current.__class__ == PlaceholderEnd:
  303.                                 last = current
  304.                 
  305.                 self.set_active_placeholder(next)
  306.                 
  307.                 if next:
  308.                         next.enter()
  309.                         
  310.                         if next.__class__ == PlaceholderEnd:
  311.                                 last = next
  312.  
  313.                 if last:
  314.                         # This is the end of the placeholder, remove the snippet etc
  315.                         for snippet in list(self.active_snippets):
  316.                                 if snippet.placeholders[0] == last:
  317.                                         self.deactivate_snippet(snippet)
  318.                                         break
  319.                 
  320.                 self.cursor_on_screen()
  321.                 
  322.                 return next != None
  323.         
  324.         def skip_to_next_placeholder(self):
  325.                 (current, next) = self.next_placeholder()
  326.                 return self.goto_placeholder(current, next)
  327.         
  328.         def skip_to_previous_placeholder(self):
  329.                 (current, prev) = self.previous_placeholder()
  330.                 return self.goto_placeholder(current, prev)
  331.  
  332.         def env_get_selected_text(self, buf):
  333.                 bounds = buf.get_selection_bounds()
  334.  
  335.                 if bounds:
  336.                         return buf.get_text(bounds[0], bounds[1])
  337.                 else:
  338.                         return ''
  339.  
  340.         def env_get_current_word(self, buf):
  341.                 start, end = buffer_word_boundary(buf)
  342.                 
  343.                 return buf.get_text(start, end)
  344.                 
  345.         def env_get_filename(self, buf):
  346.                 uri = buf.get_uri()
  347.                 
  348.                 if uri:
  349.                         return buf.get_uri_for_display()
  350.                 else:
  351.                         return ''
  352.         
  353.         def env_get_basename(self, buf):
  354.                 uri = buf.get_uri()
  355.                 
  356.                 if uri:
  357.                         return os.path.basename(buf.get_uri_for_display())
  358.                 else:
  359.                         return ''
  360.  
  361.         def update_environment(self):
  362.                 buf = self.view.get_buffer()
  363.                 
  364.                 variables = {'GEDIT_SELECTED_TEXT': self.env_get_selected_text, 
  365.                              'GEDIT_CURRENT_WORD': self.env_get_current_word, 
  366.                              'GEDIT_FILENAME': self.env_get_filename, 
  367.                              'GEDIT_BASENAME': self.env_get_basename}
  368.                 
  369.                 for var in variables:
  370.                         os.environ[var] = variables[var](buf)
  371.         
  372.         def uses_current_word(self, snippet):
  373.                 matches = re.findall('(\\\\*)\\$GEDIT_CURRENT_WORD', snippet['text'])
  374.                 
  375.                 for match in matches:
  376.                         if len(match) % 2 == 0:
  377.                                 return True
  378.                 
  379.                 return False
  380.  
  381.         def apply_snippet(self, snippet, start = None, end = None):
  382.                 if not snippet.valid:
  383.                         return False
  384.  
  385.                 buf = self.view.get_buffer()
  386.                 s = Snippet(snippet)
  387.                 
  388.                 if not start:
  389.                         start = buf.get_iter_at_mark(buf.get_insert())
  390.                 
  391.                 if not end:
  392.                         end = buf.get_iter_at_mark(buf.get_selection_bound())
  393.  
  394.                 if start.equal(end) and self.uses_current_word(s):
  395.                         # There is no tab trigger and no selection and the snippet uses
  396.                         # the current word. Set start and end to the word boundary so that 
  397.                         # it will be removed
  398.                         start, end = buffer_word_boundary(buf)
  399.  
  400.                 # Set environmental variables
  401.                 self.update_environment()
  402.                 
  403.                 # You know, we could be in an end placeholder
  404.                 (current, next) = self.next_placeholder()
  405.                 if current and current.__class__ == PlaceholderEnd:
  406.                         self.goto_placeholder(current, None)
  407.                 
  408.                 buf.begin_user_action()
  409.  
  410.                 # Remove the tag, selection or current word
  411.                 buf.delete(start, end)
  412.                 
  413.                 # Insert the snippet
  414.                 holders = len(self.placeholders)
  415.                 
  416.                 if len(self.active_snippets) == 0:
  417.                         self.first_snippet_inserted()
  418.  
  419.                 sn = s.insert_into(self, start)
  420.                 self.active_snippets.append(sn)
  421.  
  422.                 # Put cursor at first tab placeholder
  423.                 keys = filter(lambda x: x > 0, sn.placeholders.keys())
  424.                 
  425.                 if len(keys) == 0:
  426.                         buf.place_cursor(sn.begin_iter())
  427.                 else:
  428.                         self.goto_placeholder(self.active_placeholder, sn.placeholders[keys[0]])
  429.                 
  430.                 buf.end_user_action()
  431.  
  432.                 return True
  433.  
  434.         def get_tab_tag(self, buf):
  435.                 end = buf.get_iter_at_mark(buf.get_insert())
  436.                 start = end.copy()
  437.                 
  438.                 word = None
  439.                 
  440.                 if start.backward_word_start():
  441.                         # Check if we were at a word start ourselves
  442.                         tmp = start.copy()
  443.                         tmp.forward_word_end()
  444.                         
  445.                         if tmp.equal(end):
  446.                                 word = buf.get_text(start, end)
  447.                         else:
  448.                                 start = end.copy()
  449.                 else:
  450.                         start = end.copy()
  451.                 
  452.                 if not word or word == '':
  453.                         if start.backward_char():
  454.                                 word = start.get_char()
  455.  
  456.                                 if word.isalnum() or word.isspace():
  457.                                         return (None, None, None)
  458.                         else:
  459.                                 return (None, None, None)
  460.                 
  461.                 return (word, start, end)
  462.  
  463.         def run_snippet(self):        
  464.                 if not self.view:
  465.                         return False
  466.                 
  467.                 buf = self.view.get_buffer()
  468.                 
  469.                 # get the word preceding the current insertion position
  470.                 (word, start, end) = self.get_tab_tag(buf)
  471.                 
  472.                 if not word:
  473.                         return self.skip_to_next_placeholder()
  474.                 
  475.                 snippets = Library().from_tag(word, self.language_id)
  476.                 
  477.                 if snippets:
  478.                         if len(snippets) == 1:
  479.                                 return self.apply_snippet(snippets[0], start, end)
  480.                         else:
  481.                                 # Do the fancy completion dialog
  482.                                 return self.show_completion(snippets)
  483.  
  484.                 return self.skip_to_next_placeholder()
  485.         
  486.         def deactivate_snippet(self, snippet, force = False):
  487.                 buf = self.view.get_buffer()
  488.                 remove = []
  489.                 
  490.                 for tabstop in snippet.placeholders:
  491.                         if tabstop == -1:
  492.                                 placeholders = snippet.placeholders[-1]
  493.                         else:
  494.                                 placeholders = [snippet.placeholders[tabstop]]
  495.                         
  496.                         for placeholder in placeholders:
  497.                                 if placeholder in self.placeholders:
  498.                                         if placeholder in self.update_placeholders:
  499.                                                 placeholder.update_contents()
  500.                                                 
  501.                                                 self.update_placeholders.remove(placeholder)
  502.                                         elif placeholder in self.jump_placeholders:
  503.                                                 placeholder[0].leave()
  504.                                                 
  505.                                         remove.append(placeholder)
  506.  
  507.                 for placeholder in remove:
  508.                         if placeholder == self.active_placeholder:
  509.                                 self.active_placeholder = None
  510.  
  511.                         self.placeholders.remove(placeholder)
  512.                         self.ordered_placeholders.remove(placeholder)
  513.  
  514.                         placeholder.remove(force)
  515.  
  516.                 snippet.deactivate()
  517.                 self.active_snippets.remove(snippet)
  518.                 
  519.                 if len(self.active_snippets) == 0:
  520.                         self.last_snippet_removed()
  521.  
  522.         # Moves the completion window to a suitable place honoring the hint given
  523.         # by x and y. It tries to position the window so it's always visible on the
  524.         # screen.
  525.         def move_completion_window(self, complete, x, y):
  526.                 MARGIN = 15
  527.                 screen = self.view.get_screen()
  528.                 
  529.                 width = screen.get_width()
  530.                 height = screen.get_height()
  531.                 
  532.                 cw, ch = complete.get_size()
  533.                 
  534.                 if x + cw > width:
  535.                         x = width - cw - MARGIN
  536.                 elif x < MARGIN:
  537.                         x = MARGIN
  538.                 
  539.                 if y + ch > height:
  540.                         y = height - ch - MARGIN
  541.                 elif y < MARGIN:
  542.                         y = MARGIN
  543.  
  544.                 complete.move(x, y)
  545.  
  546.         # Show completion, shows a completion dialog in the view.
  547.         # If preset is not None then a completion dialog is shown with the snippets
  548.         # in the preset list. Otherwise it will try to find the word preceding the
  549.         # current cursor position. If such a word is found, it is taken as a 
  550.         # tab trigger prefix so that only snippets with a tab trigger prefixed with
  551.         # the word are in the list. If no such word can be found than all snippets
  552.         # are shown.
  553.         def show_completion(self, preset = None):
  554.                 buf = self.view.get_buffer()
  555.                 bounds = buf.get_selection_bounds()
  556.                 prefix = None
  557.                 
  558.                 if not bounds and not preset:
  559.                         # When there is no text selected and no preset present, find the
  560.                         # prefix
  561.                         (prefix, start, end) = self.get_tab_tag(buf)
  562.                 
  563.                 if not prefix:
  564.                         # If there is no prefix, than take the insertion point as the end
  565.                         end = buf.get_iter_at_mark(buf.get_insert())
  566.                 
  567.                 if not preset or len(preset) == 0:
  568.                         # There is no preset, find all the global snippets and the language
  569.                         # specific snippets
  570.                         
  571.                         nodes = Library().get_snippets(None)
  572.                         
  573.                         if self.language_id:
  574.                                 nodes += Library().get_snippets(self.language_id)
  575.                         
  576.                         if prefix and len(prefix) == 1 and not prefix.isalnum():
  577.                                 hasnodes = False
  578.                                 
  579.                                 for node in nodes:
  580.                                         if node['tag'] and node['tag'].startswith(prefix):
  581.                                                 hasnodes = True
  582.                                                 break
  583.                                 
  584.                                 if not hasnodes:
  585.                                         prefix = None
  586.                         
  587.                         complete = SnippetComplete(nodes, prefix, False)        
  588.                 else:
  589.                         # There is a preset, so show that preset
  590.                         complete = SnippetComplete(preset, None, True)
  591.                 
  592.                 complete.connect('snippet-activated', self.on_complete_row_activated)
  593.                 
  594.                 rect = self.view.get_iter_location(end)
  595.                 win = self.view.get_window(gtk.TEXT_WINDOW_TEXT)
  596.                 (x, y) = self.view.buffer_to_window_coords( \
  597.                                 gtk.TEXT_WINDOW_TEXT, rect.x + rect.width, rect.y)
  598.                 (xor, yor) = win.get_origin()
  599.                 
  600.                 self.move_completion_window(complete, x + xor, y + yor)                
  601.                 return complete.run()
  602.  
  603.         def update_snippet_contents(self):
  604.                 self.timeout_update_id = 0
  605.                 
  606.                 for placeholder in self.update_placeholders:
  607.                         placeholder.update_contents()
  608.                 
  609.                 for placeholder in self.jump_placeholders:
  610.                         self.goto_placeholder(placeholder[0], placeholder[1])
  611.                 
  612.                 del self.update_placeholders[:]
  613.                 del self.jump_placeholders[:]
  614.                 
  615.                 return False
  616.  
  617.         # Callbacks
  618.         def on_view_destroy(self, view):
  619.                 self.stop()
  620.                 return
  621.  
  622.         def on_complete_row_activated(self, complete, snippet):
  623.                 buf = self.view.get_buffer()
  624.                 bounds = buf.get_selection_bounds()
  625.                 
  626.                 if bounds:
  627.                         self.apply_snippet(snippet.data, None, None)
  628.                 else:
  629.                         (word, start, end) = self.get_tab_tag(buf)
  630.                         self.apply_snippet(snippet.data, start, end)
  631.  
  632.         def on_buffer_cursor_moved(self, buf):
  633.                 piter = buf.get_iter_at_mark(buf.get_insert())
  634.  
  635.                 # Check for all snippets if the cursor is outside its scope
  636.                 for snippet in list(self.active_snippets):
  637.                         if snippet.begin_mark.get_deleted() or snippet.end_mark.get_deleted():
  638.                                 self.deactivate(snippet)
  639.                         else:
  640.                                 begin = snippet.begin_iter()
  641.                                 end = snippet.end_iter()
  642.                         
  643.                                 if piter.compare(begin) < 0 or piter.compare(end) > 0:
  644.                                         # Oh no! Remove the snippet this instant!!
  645.                                         self.deactivate_snippet(snippet)
  646.                 
  647.                 current = self.current_placeholder()
  648.                 
  649.                 if current != self.active_placeholder:
  650.                         if self.active_placeholder:
  651.                                 self.jump_placeholders.append((self.active_placeholder, current))
  652.                                 
  653.                                 if self.timeout_update_id == 0:
  654.                                         self.timeout_update_id = gobject.timeout_add(0, 
  655.                                                         self.update_snippet_contents)
  656.  
  657.                         self.set_active_placeholder(current)
  658.         
  659.         def on_buffer_changed(self, buf):
  660.                 current = self.current_placeholder()
  661.                 
  662.                 if current:
  663.                         if not current in self.update_placeholders:
  664.                                 self.update_placeholders.append(current)
  665.                 
  666.                         if self.timeout_update_id == 0:
  667.                                 self.timeout_update_id = gobject.timeout_add(0, \
  668.                                                 self.update_snippet_contents)
  669.         
  670.         def on_buffer_insert_text(self, buf, piter, text, length):
  671.                 ctx = get_buffer_context(buf)
  672.                 
  673.                 # do nothing special if there is no context and no active 
  674.                 # placeholder
  675.                 if (not ctx) and (not self.active_placeholder):
  676.                         return
  677.                 
  678.                 if not ctx:
  679.                         ctx = self.active_placeholder
  680.                 
  681.                 if not ctx in self.ordered_placeholders:
  682.                         return
  683.                         
  684.                 # move any marks that were incorrectly moved by this insertion
  685.                 # back to where they belong
  686.                 begin = ctx.begin_iter()
  687.                 end = ctx.end_iter()
  688.                 idx = self.ordered_placeholders.index(ctx)
  689.                 
  690.                 for placeholder in self.ordered_placeholders:
  691.                         if placeholder == ctx:
  692.                                 continue
  693.                         
  694.                         ob = placeholder.begin_iter()
  695.                         oe = placeholder.end_iter()
  696.                         
  697.                         if ob.compare(begin) == 0 and ((not oe) or oe.compare(end) == 0):
  698.                                 oidx = self.ordered_placeholders.index(placeholder)
  699.                                 
  700.                                 if oidx > idx and ob:
  701.                                         buf.move_mark(placeholder.begin, end)
  702.                                 elif oidx < idx and oe:
  703.                                         buf.move_mark(placeholder.end, begin)
  704.                         elif ob.compare(begin) >= 0 and ob.compare(end) < 0 and (oe and oe.compare(end) >= 0):
  705.                                 buf.move_mark(placeholder.begin, end)
  706.                         elif (oe and oe.compare(begin) > 0) and ob.compare(begin) <= 0:
  707.                                 buf.move_mark(placeholder.end, begin)
  708.         
  709.         def on_notify_language(self, buf, spec):
  710.                 self.update_language()
  711.  
  712.         def on_notify_editable(self, view, spec):
  713.                 self._set_view(view)
  714.         
  715.         def on_view_key_press(self, view, event):
  716.                 library = Library()
  717.  
  718.                 if not (event.state & gdk.CONTROL_MASK) and \
  719.                                 not (event.state & gdk.MOD1_MASK) and \
  720.                                 event.keyval in self.TAB_KEY_VAL:
  721.                         if not event.state & gdk.SHIFT_MASK:
  722.                                 return self.run_snippet()
  723.                         else:
  724.                                 return self.skip_to_previous_placeholder()
  725.                 elif (event.state & gdk.CONTROL_MASK) and \
  726.                                 not (event.state & gdk.MOD1_MASK) and \
  727.                                 not (event.state & gdk.SHIFT_MASK) and \
  728.                                 event.keyval in self.SPACE_KEY_VAL:
  729.                         return self.show_completion()
  730.                 elif not library.loaded and \
  731.                                 library.valid_accelerator(event.keyval, event.state):
  732.                         library.ensure_files()
  733.                         library.ensure(self.language_id)
  734.                         self.accelerator_activate(event.keyval, \
  735.                                         event.state & gtk.accelerator_get_default_mod_mask())
  736.  
  737.                 return False
  738.         
  739.         def path_split(self, path, components=[]):
  740.                 head, tail = os.path.split(path)
  741.                 
  742.                 if not tail and head:
  743.                         return [head] + components
  744.                 elif tail:
  745.                         return self.path_split(head, [tail] + components)
  746.                 else:
  747.                         return components
  748.         
  749.         def relative_filename(self, first, second, mime):
  750.                 prot1 = re.match('(^[a-z]+:\/\/|\/)(.*)', first)
  751.                 prot2 = re.match('(^[a-z]+:\/\/|\/)(.*)', second)
  752.                 
  753.                 if not prot1 or not prot2:
  754.                         return second
  755.                 
  756.                 # Different protocols
  757.                 if prot1.group(1) != prot2.group(1):
  758.                         return second
  759.                 
  760.                 # Split on backslash
  761.                 path1 = self.path_split(prot1.group(2))
  762.                 path2 = self.path_split(prot2.group(2))
  763.                 
  764.                 # Remove as long as common
  765.                 while path1 and path2 and path1[0] == path2[0]:
  766.                         path1.pop(0)
  767.                         path2.pop(0)
  768.                 
  769.                 # If we need to ../ more than 3 times, then just return
  770.                 # the absolute path
  771.                 if len(path1) - 1 > 3:
  772.                         return second
  773.                 
  774.                 if mime.startswith('x-directory'):
  775.                         # directory, special case
  776.                         if not path2:
  777.                                 result = './'
  778.                         else:
  779.                                 result = '../' * (len(path1) - 1)       
  780.                 else:   
  781.                         # Insert ../
  782.                         result = '../' * (len(path1) - 1)
  783.                 
  784.                         if not path2:
  785.                                 result = os.path.basename(second)
  786.                 
  787.                 if path2:
  788.                         result += os.path.join(*path2)
  789.                         
  790.                 return result
  791.         
  792.         def apply_uri_snippet(self, snippet, mime, uri):
  793.                 # Remove file scheme
  794.                 if gedit.utils.uri_has_file_scheme(uri):
  795.                         gfile = gio.File(uri)
  796.                         uri = gfile.get_path()
  797.                 
  798.                 # Set environmental variables
  799.                 filename = self.env_get_filename(self.view.get_buffer())
  800.                 
  801.                 os.environ['GEDIT_DROP_FILENAME'] = uri
  802.                 os.environ['GEDIT_DROP_MIME_TYPE'] = mime
  803.                 os.environ['GEDIT_DROP_REL_FILENAME'] = self.relative_filename(filename, uri, mime)
  804.  
  805.                 buf = self.view.get_buffer()
  806.                 mark = buf.get_mark('gtk_drag_target')
  807.                 
  808.                 if not mark:
  809.                         mark = buf.get_insert()
  810.  
  811.                 piter = buf.get_iter_at_mark(mark)
  812.                 self.apply_snippet(snippet, piter, piter)
  813.  
  814.         def in_bounds(self, x, y):
  815.                 rect = self.view.get_visible_rect()
  816.                 rect.x, rect.y = self.view.buffer_to_window_coords(gtk.TEXT_WINDOW_WIDGET, rect.x, rect.y)
  817.  
  818.                 return not (x < rect.x or x > rect.x + rect.width or y < rect.y or y > rect.y + rect.height)
  819.         
  820.         def on_drag_data_received(self, view, context, x, y, data, info, timestamp):   
  821.                 if not (gtk.targets_include_uri(context.targets) and data.data and self.in_bounds(x, y)):
  822.                         return
  823.  
  824.                 uris = drop_get_uris(data)
  825.                 uris.reverse()
  826.                 stop = False
  827.                 
  828.                 for uri in uris:
  829.                         try:
  830.                                 mime = gio.content_type_guess(uri)
  831.                         except:
  832.                                 mime = None
  833.  
  834.                         if not mime:
  835.                                 continue
  836.                         
  837.                         snippets = Library().from_drop_target(mime, self.language_id)
  838.                         
  839.                         if snippets:
  840.                                 stop = True
  841.                                 self.apply_uri_snippet(snippets[0], mime, uri)
  842.  
  843.                 if stop:
  844.                         context.finish(True, False, timestamp)
  845.                         view.stop_emission('drag-data-received')
  846.                         view.get_toplevel().present()
  847.                         view.grab_focus()
  848.         
  849.         def find_uri_target(self, context):
  850.                 lst = gtk.target_list_add_uri_targets((), 0)
  851.                 
  852.                 return self.view.drag_dest_find_target(context, lst)
  853. # ex:ts=8:et:
  854.